package utils

import (
	"errors"
	"fmt"
	"os/exec"
	"strings"
)

const QUOTECHR = '"'

func ExecCommand(cmdline string) *exec.Cmd {
	args, err := ParseCommandLine(cmdline)
	if err != nil {
		return nil
	}
	if len(args) == 1 {
		return exec.Command(args[0])
	} else {
		return exec.Command(args[0], args[1:]...)
	}
}

type CmdArgs struct {
	firstKey string
	args     StrMap
}

func NewCmdArgs() *CmdArgs {
	rval := &CmdArgs{}
	return rval
}

func ParseCmdArgs(s string) *CmdArgs {
	rval := &CmdArgs{}
	rval.Parse(s)
	return rval
}

func (this *CmdArgs) GetFirstKey() string {
	return this.firstKey
}

func (this *CmdArgs) HasKeyWord(key string) bool {
	return this.args[key] == key
}

func (this *CmdArgs) GetArgAsString(name string) string {
	return this.args[name]
}

func (this *CmdArgs) StringByName(name string, def string) string {
	if v, ok := this.args[name]; ok {
		return v
	} else {
		return def
	}
}

func (this *CmdArgs) Encode() string {
	var sb BytesBuilder
	sb.AppendStr(this.firstKey)
	if len(this.args) > 0 {
		this.args.SortRange(func(k, v string) bool {
			if sb.Len() > 0 {
				sb.AppendStr(" ")
			}
			if len(v) > 0 {
				if strings.Contains(v, " ") {
					sb.Appendf("%s=\"%s\"", k, EscapeJsonStrSimple(v))
				} else {
					sb.Appendf("%s=%s", k, EscapeJsonStrSimple(v))
				}
			} else {
				sb.Appendf("%s", k)
			}
			return true
		})
	}
	return sb.String()
}

func (this *CmdArgs) GetArgAsInt(name string, def int) int {
	return StrToIntDef(this.args[name], def)
}

func (this *CmdArgs) GetArgAsInt64(name string, def int64) int64 {
	return StrToInt64Def(this.args[name], def)
}

func (this *CmdArgs) Reset() {
	if len(this.args) > 0 {
		for k, _ := range this.args {
			delete(this.args, k)
		}
	}

	this.firstKey = ""
}

func (this *CmdArgs) ParseArgs(args []string) {
	if this.args == nil {
		this.args = NewStrMap0()
	} else {
		for k, _ := range this.args {
			delete(this.args, k)
		}
	}

	for i := 0; i < len(args); i++ {
		s1, s2 := Split2Str(args[i], "=")

		if len(s1) > 0 {
			if s1[0] == '-' {
				s1 = s1[1:]
			}
			if len(s2) == 0 {
				s2 = s1
				if i == 0 {
					this.firstKey = s1
				} else {
					this.args[s1] = s2
				}
			} else {
				_, s2 = CheckTrimQuoteChr(s2, QUOTECHR, QUOTECHR)
				this.args[s1] = s2
			}

		}
	}
}

func (this *CmdArgs) Parse(raw string) error {
	strs, err := ParseCommandLine(raw)
	if err != nil {
		return err
	}

	this.ParseArgs(strs)

	return nil
}

func ParseCommandLineBK(command string) ([]string, error) {
	var args []string
	state := "start"
	current := ""
	quote := "\""
	escapeNext := true
	for i := 0; i < len(command); i++ {
		c := command[i]

		if state == "quotes" {
			if string(c) != quote {
				current += string(c)
			} else {
				args = append(args, current)
				current = ""
				state = "start"
			}
			continue
		}

		if escapeNext {
			current += string(c)
			escapeNext = false
			continue
		}

		//if (c == '\\') {
		//	escapeNext = true
		//	continue
		//}

		if c == '"' || c == '\'' {
			state = "quotes"
			quote = string(c)
			continue
		}

		if state == "arg" {
			if c == ' ' || c == '\t' {
				args = append(args, current)
				current = ""
				state = "start"
			} else {
				current += string(c)
			}
			continue
		}

		if c != ' ' && c != '\t' {
			state = "arg"
			current += string(c)
		}
	}

	if state == "quotes" {
		return []string{}, errors.New(fmt.Sprintf("Unclosed quote in command line: %s", command))
	}

	if current != "" {
		args = append(args, current)
	}

	return args, nil
}

func ParseCommandLine(command string) ([]string, error) {
	return ParseCommandLineEx(command), nil
}

/*
*
 */
func ParseCommandLineEx(command string) (args []string) {
	var quotestate = 0 // 0: none, 1:quotestart
	var quotechar rune
	runeBuf := []rune(command)
	runeB := make([]rune, 0, 1024)
	idx := 0  // 参数序号
	step := 0 // 1：已经开始
	for i := 0; i < len(runeBuf); i++ {
		chr := runeBuf[i]
		if quotestate == 0 && (chr == QUOTECHR || chr == '\'') { // quote start
			quotestate = 1
			quotechar = chr
			runeB = append(runeB, chr)
		} else if quotestate == 1 && (chr == quotechar) { // split end
			quotestate = 0
			runeB = append(runeB, chr)
		} else if chr == ' ' || chr == '\t' {
			if quotestate == 1 {
				runeB = append(runeB, chr)
			} else if step == 1 {
				args = append(args, string(runeB))
				runeB = runeB[:0]
				quotestate = 0
				step = 0
				idx++
			}
		} else {
			if step == 0 {
				step = 1
			}
			runeB = append(runeB, chr)
		}
	}

	if len(runeB) > 0 {
		args = append(args, string(runeB))
		idx++
	}

	return
}
